package com.mtw.bbs.article.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mtw.bbs.constant.ArticleConstant;
import com.mtw.bbs.article.pojo.elasticsearch.ArticleESIndex;
import com.mtw.bbs.article.pojo.entity.ArticleIndex;
import com.mtw.bbs.article.mapper.ArticleIndexMapper;
import com.mtw.bbs.article.pojo.entity.ArticleSubject;
import com.mtw.bbs.article.pojo.entity.ArticleFile;
import com.mtw.bbs.article.pojo.form.ArticleForm;
import com.mtw.bbs.article.pojo.vo.ArticleVo;
import com.mtw.bbs.article.pojo.vo.SimpleArticleVo;
import com.mtw.bbs.article.pojo.vo.TopicVo;
import com.mtw.bbs.article.repository.ArticleESRepository;
import com.mtw.bbs.article.service.*;
import com.mtw.bbs.util.ArticleComponent;
import com.mtw.bbs.util.ArticleUtils;
import com.mtw.bbs.common.core.constant.FileConstant;
import com.mtw.bbs.common.core.security.UserDetails;
import com.mtw.bbs.common.core.security.context.SecurityContextHolder;
import com.mtw.bbs.common.core.util.BeanCopyUtils;
import com.mtw.bbs.common.core.util.FileUtil;
import com.mtw.bbs.common.core.vo.BaseFileVo;
import com.mtw.bbs.common.core.vo.Result;
import com.mtw.bbs.common.redisBase.util.RedisUtilz;
import com.mtw.bbs.common.redisBase.util.TimeUtilz;
import com.mtw.bbs.userAdmin.pojo.dto.UserDto;
import com.mtw.bbs.userAdmin.feign.UserClient;
import com.mtw.bbs.userAdmin.pojo.vo.UserInfoVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 文章逻辑层
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ArticleIndexServiceImpl extends ServiceImpl<ArticleIndexMapper, ArticleIndex> implements ArticleIndexService {


    private final UserClient userClient;
    private final ArticleComponent articleComponent;
    private final ThreadPoolTaskExecutor bizExecutor;
    private final TopicIndexService topicIndexService;
    private final ArticleIndexMapper articleIndexMapper;
    private final ArticleFileService articleFileService;
    private final ArticleESRepository articleESRepository;
    private final RedisUtilz<String,ArticleIndex> redisUtilz;
    private final ArticleSubjectService ArticleSubjectService;





    /**
     * 分页获取dto
     *
     * @param page      分页对象
     * @param lqw       查询条件
     * @return 分页结果
     */
    @Override
    public Page<ArticleVo> getArticlePage(int page, int size, LambdaQueryWrapper<ArticleIndex> lqw) {

        long s = System.currentTimeMillis();

        Page<ArticleIndex> entityPage = articleIndexMapper.selectPage(new Page<>(page, size), lqw);
        if (entityPage.getRecords().isEmpty()){
            return new Page<>(page,size,entityPage.getTotal());
        }
        long e1 = System.currentTimeMillis();
        log.info("文章查询数据库耗时：{}",e1-s);


        // 共享线程数据
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();


        Map<String, String> id_uid_map = new HashMap<>(size);
        Map<String, String> id_topic_map = new HashMap<>(size);
        // 当前用户
        UserDetails user = SecurityContextHolder.getContext().getAuthentication();

        // 获取发帖人信息
        CompletableFuture<Map<String, UserInfoVo>> userF = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // 发帖人id
            List<String> uidList = entityPage.getRecords().stream().map(ArticleIndex::getCreatedBy).toList();
            return ArticleUtils.getUserInfoVoMap2R(userClient.getUserInfoByIds(uidList.toArray(String[]::new)));
        }, bizExecutor);


        HashSet<String> topicIdSet = new HashSet<>(size);
        // 构造VO
        List<ArticleVo> voList= entityPage.getRecords().stream().map(x -> {
            id_uid_map.put(x.getId(),x.getCreatedBy());
            id_topic_map.put(x.getId(),x.getTopicId());
            ArticleVo vo = BeanCopyUtils.copyBean(x, ArticleVo.class);
            topicIdSet.add(x.getTopicId());
            return vo;
        }).toList();

        // 话题信息
        CompletableFuture<HashMap<String, TopicVo>> topicF = CompletableFuture.supplyAsync(() -> {
            HashMap<String, TopicVo> topicMap = new HashMap<>(size);
            topicIdSet.forEach(x -> {
                TopicVo topicInfo = topicIndexService.getTopicInfo(x);
                topicMap.put(x, topicInfo);
            });
            return topicMap;
        }, bizExecutor);


        long e2 = System.currentTimeMillis();
        log.info("构造文章VO耗时：{}",e2-e1);

        Page<ArticleVo> voPage = new Page<>(page, size, entityPage.getTotal());
        voPage.setRecords(voList);
        // 设置评论、点赞等信息
        articleComponent.setArticleSubject(voPage.getRecords(),user == null?null:user.getId());


        log.info("设置评论、点赞等信息耗时：{}",System.currentTimeMillis()-e2);

        try {
            Map<String, UserInfoVo> posterVoMap =  userF.get(8, TimeUnit.SECONDS);
            Map<String, TopicVo> topicVoMap = topicF.get(8, TimeUnit.SECONDS);
            voPage.getRecords().forEach(x -> {
                // 发帖人信息
                if (posterVoMap != null){
                    x.setPoster(posterVoMap.get(id_uid_map.get(x.getId())));
                }
                // 话题信息
                if (topicVoMap != null){
                    x.setTopicInfo(topicVoMap.get(id_topic_map.get(x.getId())));
                }
            });
        } catch (Exception e) {
            log.error("帖子异步获取用户信息异常");
        }

        return voPage;
    }






    /**
     * 根据id获取简单的文章信息
     *
     * @param ids 文章id
     * @return 简单的文章信息
     */
    @Override
    public List<SimpleArticleVo> getSimpleArticle(Collection<String> ids) {

        LambdaQueryWrapper<ArticleIndex> lqw = new LambdaQueryWrapper<>();
        lqw.select(ArticleIndex::getId,ArticleIndex::getContent,ArticleIndex::getOutsideLink,
                ArticleIndex::getApproveStatus,ArticleIndex::getCreatedDate);
        lqw.in(ArticleIndex::getId,ids);
        List<ArticleIndex> list = list(lqw);
        if (CollectionUtil.isEmpty(list)){
            return new ArrayList<>(0);
        }
        // 查附件
        Map<String, List<BaseFileVo>> fileMap = articleFileService.getFileMap(ids.stream().toList());
        return  list.stream().map(x -> {
            SimpleArticleVo vo = BeanCopyUtils.copyBean(x, SimpleArticleVo.class);
            vo.setFileList(fileMap.get(vo.getId()));
            return vo;
        }).toList();
    }


    /**
     * 获取文章详情
     *
     * @param id 文章id
     */
    @Override
    public ArticleVo getInfoById(String id) {

        // 查缓存
        String key = ArticleConstant.ARTICLE_REDIS_KEY + id;
        ArticleIndex artitcle = redisUtilz.get(key);

        // 缓存不存在去查数据库
        if (artitcle == null) {
            artitcle = articleIndexMapper.selectById(id);
            if (artitcle == null){
                return null;
            }
            redisUtilz.set(key,artitcle, Duration.ofSeconds(300+TimeUtilz.getRandomSecond()));
        }

        ArticleVo vo = BeanCopyUtils.copyBean(artitcle, ArticleVo.class);

        // 共享线程数据
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        // 查询附件
        CompletableFuture<Boolean> fileF = CompletableFuture.supplyAsync(() -> {
            List<BaseFileVo> fileList = articleFileService.getFileList(id);
            vo.setFileList(fileList);
            return true;
        }, bizExecutor);


        // 获取发帖人信息
        String createdBy = artitcle.getCreatedBy();
        CompletableFuture<Boolean> posterF = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            Result<UserDto> userResult = userClient.getUserInfoById(createdBy);
            UserInfoVo poster = ArticleUtils.userInfoVoToR(userResult);
            vo.setPoster(poster);
            return true;
        }, bizExecutor);


        // 查询话题
        TopicVo topicInfo = topicIndexService.getTopicInfo(artitcle.getTopicId());
        vo.setTopicInfo(topicInfo);

        try {
            fileF.get(8, TimeUnit.SECONDS);
            posterF.get(8, TimeUnit.SECONDS);
        }catch (Exception e){
            log.error("异步获发帖人和话题信息异常",e);
        }

        return vo;
    }


    /**
     * 保存话题文章
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String saveArticle(ArticleForm form) {

        if (form == null) {
            return null;
        }
        // 保存实体
        ArticleIndex articleIndex = new ArticleIndex();
        BeanUtils.copyProperties(form, articleIndex);
        articleIndex.setContentType(0);
        form.getFileList().forEach(x -> {
            Integer type = FileUtil.getTypeByFile(x.getFileSuffix());
            if (FileConstant.FILE_VIDEO.equals(type)) {
                articleIndex.setContentType(1);
            }
        });
        articleIndexMapper.insert(articleIndex);

        // 保存附件
        if (CollectionUtil.isNotEmpty(form.getFileList())) {
            List<ArticleFile> files = form.getFileList().stream().map(x -> {
                ArticleFile file = new ArticleFile();
                file.setFileId(x.getId());
                file.setArticleId(articleIndex.getId());
                return file;
            }).toList();
            articleFileService.saveBatch(files);
        }

        ArticleSubject extend = new ArticleSubject();
        extend.setId(articleIndex.getId());
        extend.setShareCount(0);
        ArticleSubjectService.save(extend);


        // 保存ES
        ArticleESIndex esIndex = new ArticleESIndex();
        BeanUtils.copyProperties(articleIndex, esIndex);
        articleESRepository.save(esIndex);

        return articleIndex.getId();
    }


    /**
     * 更新文章
     */
    @Override
    @Transactional
    public boolean updateArticle(ArticleForm form) {

        ArticleIndex articleIndex = new ArticleIndex();
        BeanUtils.copyProperties(form, articleIndex);
        articleIndex.setContentType(0);
        if (CollectionUtil.isNotEmpty(form.getFileList())){
            form.getFileList().forEach(x -> {
                Integer type = FileUtil.getTypeByFile(x.getFileSuffix());
                if (FileConstant.FILE_VIDEO.equals(type)) {
                    articleIndex.setContentType(1);
                }
            });
        }
        boolean updated = updateById(articleIndex);
        if (!updated) {
            return false;
        }

        // 删除附件
        LambdaQueryWrapper<ArticleFile> ldw = new LambdaQueryWrapper<>();
        ldw.eq(ArticleFile::getArticleId,form.getId());
        articleFileService.remove(ldw);
        // 保存新附件
        if (CollectionUtil.isNotEmpty(form.getFileList())) {
            List<ArticleFile> files = form.getFileList().stream().map(x -> {
                ArticleFile file = new ArticleFile();
                file.setFileId(x.getId());
                file.setArticleId(articleIndex.getId());
                return file;
            }).toList();
            articleFileService.saveBatch(files);
        }

        // 保存ES
        ArticleIndex entity = getById(form.getId());
        ArticleESIndex esIndex = new ArticleESIndex();
        BeanUtils.copyProperties(entity, esIndex);
        articleESRepository.save(esIndex);
        return true;
    }


    /**
     * 更新文章
     */
    @Override
    @Transactional
    public boolean updateArticleByIds(LambdaUpdateWrapper<ArticleIndex> luw, Collection<String> ids) {

        boolean updated = update(luw);
        if (!updated) {
            return false;
        }
        if (CollectionUtil.isNotEmpty(ids)) {
            List<ArticleIndex> articleIndices = listByIds(ids);
            if (CollectionUtil.isNotEmpty(articleIndices)) {
                articleIndices.forEach(x -> {
                    ArticleESIndex esIndex = new ArticleESIndex();
                    BeanUtils.copyProperties(x, esIndex);
                    articleESRepository.save(esIndex);
                });
            }
        }
        return true;
    }


    /**
     * 用户更新自己的文章
     */
    @Override
    @Transactional
    public boolean updateArticleByAuthor(ArticleForm form) {

        UserDetails user = SecurityContextHolder.getContext().getAuthentication();
        if (form == null) {
            return false;
        }
        LambdaUpdateWrapper<ArticleIndex> luw = new LambdaUpdateWrapper<>();
        luw.set(StringUtils.isNotBlank(form.getContent()), ArticleIndex::getContent, form.getContent());
        luw.set(form.getStatus() != null, ArticleIndex::getStatus, form.getStatus());
        luw.eq(ArticleIndex::getId, form.getId());
        luw.eq(ArticleIndex::getCreatedBy, user.getId());
        boolean updated = update(luw);
        if (!updated) {
            return false;
        }
        // 保存ES
        ArticleIndex entity = getById(form.getId());
        ArticleESIndex esIndex = new ArticleESIndex();
        BeanUtils.copyProperties(entity, esIndex);
        articleESRepository.save(esIndex);
        return true;
    }


    /**
     * 删除文章
     *
     * @param ids 文章id集合
     * @return 删除结果
     */
    @Override
    @Transactional
    public boolean deleteArticle(Collection<String> ids) {

        if (CollectionUtil.isEmpty(ids)) {
            return false;
        }
        boolean deleted = removeBatchByIds(ids);
        if (deleted) {
            articleESRepository.deleteAllById(ids);
        }
        return deleted;
    }




    /**
     * 用户删除自己的文章
     *
     * @param id 文章id
     * @return 删除结果
     */
    @Override
    @Transactional
    public boolean deleteArticleByAuthor(String id) {

        UserDetails user = SecurityContextHolder.getContext().getAuthentication();
        LambdaQueryWrapper<ArticleIndex> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ArticleIndex::getId, id);
        lqw.eq(ArticleIndex::getCreatedBy, user.getId());
        boolean deleted = remove(lqw);
        if (deleted) {
            articleESRepository.deleteById(id);
        }
        return deleted;
    }





}
